package cn.congine.wizarpos.mall.service.impl;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service;

import cn.congine.wizarpos.mall.exception.MangoException;
import cn.congine.wizarpos.mall.model.SysInTicket;
import cn.congine.wizarpos.mall.model.SysWxChildMerchantToken;
import cn.congine.wizarpos.mall.model.SysWxPrimaryMerchantToken;
import cn.congine.wizarpos.mall.service.SysInTicketService;
import cn.congine.wizarpos.mall.service.SysMerchantDefService;
import cn.congine.wizarpos.mall.service.SysWxChildMerchantTokenService;
import cn.congine.wizarpos.mall.service.SysWxPrimaryMerchantTokenService;
import cn.congine.wizarpos.mall.utils.SysConstants;
import cn.congine.wizarpos.mall.utils.WxSignUtil;
import cn.congine.wizarpos.mall.vo.WxAccessToken;

import com.alibaba.fastjson.JSONObject;

@Service("wxThirdAuthentication")
public class WxThirdAuthentication {

	@Resource(name = "messageSource")
	private MessageSource messageSource;

	@Autowired
	private WPosHttpClient wposHttpClient;
	@Autowired
	private SysMerchantDefService merchantDefService;
	@Autowired
	private WxAuthentication wxAuthentication;
	@Autowired
	private SysInTicketService sysInTicketService;
	@Autowired
	private SysWxPrimaryMerchantTokenService sysWxPrimaryMerchantTokenService;
	@Autowired
	private SysWxChildMerchantTokenService sysWxChildMerchantTokenService;

	/**
	 * 获取授权Token
	 * 
	 * @return
	 * @throws Exception
	 * @throws MangoException
	 */
	public WxAccessToken getAccessToken(String mid) throws Exception {
		// 根据对应的授权appid从数据库中获取对应的授权信息
		SysWxChildMerchantToken wxAuthorizationInfo = sysWxChildMerchantTokenService.getByWxAppIdAndMidAndPrimaryId(mid);
		
		WxAccessToken wxAuthToken = new WxAccessToken();
		if (wxAuthorizationInfo != null) { // 必须存在，不存在则未授权
			// 如果授权信息存在，则判断当前的授权acc_token是否在有效期内
			// 当前的秒数
			long now = WxSignUtil.createTimestamp();
			
			if (StringUtils.isEmpty(wxAuthorizationInfo.getPrimaryId())) {
				wxAuthToken = wxAuthentication.getAccessToken(mid);
			} else {
				System.out.println("----------WxThirdAuthentication.getAccessToken------------");
				// 数据库中授权之后的有效期秒数
				long inTime = wxAuthorizationInfo.getAuthorizerAccessTokenExpiry();
				if (now > inTime || isInvalid(wxAuthorizationInfo.getAuthorizerAccessToken()) ) {
					// 失效则刷新对应的token
					wxAuthorizationInfo = refreshSysWxChildMerchantToken(mid);
				} 
				wxAuthToken.setAccessToken(wxAuthorizationInfo.getAuthorizerAccessToken());
				wxAuthToken.setTimestamp(wxAuthorizationInfo.getAuthorizerAccessTokenExpiry());
			}
		} 
		
		return wxAuthToken;
	}

	/**
	 * 刷新授权Token
	 * 
	 * @return
	 * @throws Exception
	 * @throws MangoException
	 */
	private SysWxChildMerchantToken refreshSysWxChildMerchantToken(String mid)
			throws Exception {
		System.out.println("----------WxThirdAuthentication.refreshSysWxChildMerchantToken------------");
		// 获取对应的第三方开放平台的授权
		SysWxPrimaryMerchantToken wxComponentAccessInfo = getComponentAccessInfo(SysConstants.COMPONENT_APP_ID);

		// 获取最新的componentAccessToken
		String componentAccessToken = wxComponentAccessInfo.getComponentAccessToken();

		// 根据对应的授权appid从数据库中获取对应的授权信息
		SysWxChildMerchantToken sysWxChildMerchantToken = sysWxChildMerchantTokenService.getByWxAppIdAndMidAndPrimaryId(mid);

		String consumeUrl = SysConstants.API_AUTHORIZER_TOKEN_URL.replace(
				SysConstants.API_COMPONENT_TOKEN, componentAccessToken);

		JSONObject parms = new JSONObject();
		parms.put("component_appid", SysConstants.COMPONENT_APP_ID);
		parms.put("authorizer_appid", sysWxChildMerchantToken.getWxAppId());
		parms.put("authorizer_refresh_token",
				sysWxChildMerchantToken.getAuthorizerRefreshToken());

		// 微信请求获取授权商户信息
		JSONObject resultJson = wposHttpClient.post(consumeUrl, parms);

		// 更新下对应的授权信息
		if (resultJson.containsKey("authorizer_access_token")) {

			sysWxChildMerchantToken.setAuthorizerAccessToken(resultJson
					.getString("authorizer_access_token"));
			sysWxChildMerchantToken.setAuthorizerAccessTokenExpiry(WxSignUtil.createTimestamp()
					+ resultJson.getLong("expires_in"));
			sysWxChildMerchantToken.setAuthorizerRefreshToken(resultJson
					.getString("authorizer_refresh_token"));
			sysWxChildMerchantTokenService.update(sysWxChildMerchantToken);
		} else {
			throw new Exception("请求微信第三方子商户accessToken失败");
		}

		return sysWxChildMerchantToken;
	}
	
	
	/**
	 * 更新对应的component_access_token
	 * 
	 * @return
	 * @throws Exception
	 * @throws MangoException
	 */
	private SysWxPrimaryMerchantToken getComponentAccessInfo (String appid)
			throws Exception {
		System.out.println("----------WxThirdAuthentication.getComponentAccessInfo------------");
		// 获取第三方平台授权信息
		SysWxPrimaryMerchantToken wxComponentAccessInfo = sysWxPrimaryMerchantTokenService.getByComponentAppid(appid);
		// 判断该当前的AccessToken是否在有效期内
		// 当前的秒数
		long now = WxSignUtil.createTimestamp();
		// 存入时间的秒数
		long expirey = wxComponentAccessInfo.getComponentAccessTokenExpirey();
		if (wxComponentAccessInfo.getComponentAccessToken() == null
				|| now > expirey 
				|| isInvalid(wxComponentAccessInfo.getComponentAccessToken())) {
			System.out.println("----------WxThirdAuthentication.getComponentAccessInfo-------now = " + now + " expirey = " + expirey);
			JSONObject posJsonPams = new JSONObject();
			posJsonPams.put("component_verify_ticket",
					wxComponentAccessInfo.getComponentVerifyTicket());
			posJsonPams.put("component_appid",
					wxComponentAccessInfo.getComponentAppid());
			posJsonPams.put("component_appsecret",
					wxComponentAccessInfo.getComponentAppsecret());
	
			// 请求微信平台，重现获取母商户的component_access_token
			JSONObject resultJson = wposHttpClient.post(
					SysConstants.API_COMPONENT_TOKEN_URL, posJsonPams);
			
			if (resultJson.containsKey("component_access_token")) {
				wxComponentAccessInfo.setComponentAccessToken(resultJson
						.getString("component_access_token"));
				wxComponentAccessInfo.setComponentAccessTokenExpirey(now + resultJson.getLong("expires_in"));
			} else {
				throw new Exception("请求微信第三方母商户accessToken失败");
			}
			
			sysWxPrimaryMerchantTokenService.update(wxComponentAccessInfo);
		}
		
		return wxComponentAccessInfo;
	}
	
	/**
	 * 获取 微信 js api ticket
	 * 
	 * @param mid
	 * @return
	 * @throws MangoException
	 */
	public String getJsTicket(String mid) throws Exception {
		SysInTicket jsTicket = sysInTicketService.findByApiSymbolAndMid(
				SysConstants.JSNNAME, mid);
		
		String ticket = null;
		
		SysWxChildMerchantToken wxAuthorizationInfo = sysWxChildMerchantTokenService.getByWxAppIdAndMidAndPrimaryId(mid);
		
		if (StringUtils.isEmpty(wxAuthorizationInfo.getPrimaryId())) {// 商户未授权，使用一般access token
			ticket = wxAuthentication.getJsTicket(mid);
			return ticket;
		} 
		
		System.out.println("----------WxThirdAuthentication.getJsTicket------------");
		String accessToken = wxAuthorizationInfo.getAuthorizerAccessToken();
		Long timestamp = wxAuthorizationInfo.getAuthorizerAccessTokenExpiry();

		long now = WxSignUtil.createTimestamp();
		if (accessToken == null || (now - timestamp) > 7200L
				|| isInvalid(accessToken)) {// 过期以access token 的时间为准,
													// accessToken过期，ticket肯定无效
			SysWxChildMerchantToken wxAccessToken = refreshSysWxChildMerchantToken(mid);
			accessToken = wxAccessToken.getAuthorizerAccessToken();
			timestamp = wxAccessToken.getMerchantAccessTokenExpiry();
			ticket = updateJsTicket(accessToken, timestamp, mid);
		} else if (jsTicket == null || jsTicket.getWxTicket() == null
				|| (now - jsTicket.getExpiryTime()) > 7200L) {
			// accessToken有效，ticket不一定有效，仍要判断时间，因为accesstoken和ticket不是一一对应关系，
			// 此情况下更新ticket
			ticket = updateJsTicket(accessToken, timestamp, mid);
		} else {
			ticket = jsTicket.getWxTicket();
		}

		return ticket;
	}

	/**
	 * 获取 微信 js api ticket
	 * 
	 * @param mid
	 * @return
	 * @throws MangoException
	 */
	public String getCardTicket(String mid) throws Exception {
		SysInTicket sysInTicket = sysInTicketService.findByApiSymbolAndMid(
				SysConstants.CARDNAME, mid);
		
		String ticket = null;
		
		SysWxChildMerchantToken wxAuthorizationInfo = sysWxChildMerchantTokenService.getByWxAppIdAndMidAndPrimaryId(mid);
		
		if (StringUtils.isEmpty(wxAuthorizationInfo.getPrimaryId())) {// 商户未授权，使用一般access token
			ticket = wxAuthentication.getCardTicket(mid);
			return ticket;
		} 
		
		System.out.println("----------WxThirdAuthentication.getCardTicket------------");

		String accessToken = wxAuthorizationInfo.getAuthorizerAccessToken();
		Long timestamp = wxAuthorizationInfo.getAuthorizerAccessTokenExpiry();

		long now = WxSignUtil.createTimestamp();
		if (accessToken == null || (now - timestamp) > 7200L
				|| isInvalid(accessToken)) {// 过期以access token 的时间为准
			SysWxChildMerchantToken wxAccessToken = refreshSysWxChildMerchantToken(mid);
			accessToken = wxAccessToken.getAuthorizerAccessToken();
			timestamp = wxAccessToken.getMerchantAccessTokenExpiry();
			ticket = updateApiTicket(accessToken, timestamp, mid);
		} else if (sysInTicket == null || sysInTicket.getWxTicket() == null
				|| (now - sysInTicket.getExpiryTime()) > 7200L) {
			// accessToken有效，ticket不一定有效，仍要判断时间，因为accesstoken和ticket不是一一对应关系，
			// 此情况下更新ticket
			ticket = updateApiTicket(accessToken, timestamp, mid);
		} else {
			ticket = sysInTicket.getWxTicket();
		}

		return ticket;
	}

	/**
	 * 调用一个借口来验证accesstoken是否有效
	 * 
	 * @param accessToken
	 * @param mid
	 * @return
	 * @throws Exception
	 */
	private boolean isInvalid(String accessToken) throws Exception {
		String jsapiTicketUrl = SysConstants.GET_IP_URL.replace(
				SysConstants.ACCESS_TOKEN, accessToken);

		JSONObject result = null;
		try {
			result = wposHttpClient.get(jsapiTicketUrl);
		} catch (Exception e) {
			e.printStackTrace();
			return true;
		}
		String errcode = result.getString("errcode");
		if (errcode != null && ("40001".equals(errcode) || "40014".equals(errcode) || "42001".equals(errcode))) {
			return true;
		}
		return false;
	}

	/**
	 * 请求微信js api ticket，并更新，有效时间以access token的过期时间为准
	 * 
	 * @param accessToken
	 * @param mid
	 * @return
	 * @throws MangoException
	 */
	private String updateJsTicket(String accessToken, Long timestamp, String mid)
			throws Exception {
		System.out.println("----------WxThirdAuthentication.updateJsTicket------------");

		String jsapiTicketUrl = SysConstants.JS_API_TICKET_URL.replace(
				SysConstants.ACCESS_TOKEN, accessToken);

		JSONObject result = wposHttpClient.get(jsapiTicketUrl);

		String jsapi_ticket = result.getString("ticket");
		// 处理特殊情况。重新更新access token
		if (jsapi_ticket == null) {
			SysWxChildMerchantToken wxAccessToken = refreshSysWxChildMerchantToken(mid);
			accessToken = wxAccessToken.getAuthorizerAccessToken();
			timestamp = wxAccessToken.getMerchantAccessTokenExpiry();
			jsapiTicketUrl = SysConstants.JS_API_TICKET_URL.replace(
					SysConstants.ACCESS_TOKEN, accessToken);
			result = wposHttpClient.get(jsapiTicketUrl);
			jsapi_ticket = result.getString("ticket");
		}
		
		// 再次请求后还未获取到js ticket 就报错
		if (jsapi_ticket == null) {
			throw new Exception("请求微信js ticket失败");
		}
		
		SysInTicket sysInTicket = sysInTicketService.findByApiSymbolAndMid(
				SysConstants.JSNNAME, mid);
		
		// 删除原有记录,WxTicket为主键不能做update操作
		if (sysInTicket != null) {
			sysInTicketService.del(sysInTicket);
		}
		// 产生新纪录
		sysInTicket = new SysInTicket();
		sysInTicket.setApiSymbol(SysConstants.JSNNAME);
		sysInTicket.setMid(mid);
		sysInTicket.setWxTicket(jsapi_ticket);
		sysInTicket.setExpiryTime(timestamp);
		sysInTicketService.saveSysInTicket(sysInTicket);
		
		return jsapi_ticket;
	}

	/**
	 * 请求微信js api ticket，并更新，有效时间以access token的过期时间为准
	 * 
	 * @param accessToken
	 * @param mid
	 * @return
	 * @throws MangoException
	 */
	private String updateApiTicket(String accessToken, Long timestamp,
			String mid) throws Exception {
		String jsapiTicketUrl = SysConstants.CARD_API_TICKET_URL.replace(
				SysConstants.ACCESS_TOKEN, accessToken);

		JSONObject result = wposHttpClient.get(jsapiTicketUrl);

		String jsapi_ticket = result.getString("ticket");
		// 处理特殊情况。重新更新access token,再请求ticket一次
		if (jsapi_ticket == null) {
			SysWxChildMerchantToken wxAccessToken = refreshSysWxChildMerchantToken(mid);
			accessToken = wxAccessToken.getAuthorizerAccessToken();
			timestamp = wxAccessToken.getMerchantAccessTokenExpiry();
			jsapiTicketUrl = SysConstants.CARD_API_TICKET_URL.replace(
					SysConstants.ACCESS_TOKEN, accessToken);
			result = wposHttpClient.get(jsapiTicketUrl);
			jsapi_ticket = result.getString("ticket");
		}

		// 再次请求后还未获取到js ticket 就报错
		if (jsapi_ticket == null) {
			throw new Exception("请求微信card ticket失败");
		}
		
		SysInTicket sysInTicket = sysInTicketService.findByApiSymbolAndMid(
				SysConstants.CARDNAME, mid);

		// 删除原有记录
		if (sysInTicket != null) {
			sysInTicketService.del(sysInTicket);
		}
		// 产生新纪录
		sysInTicket = new SysInTicket();
		sysInTicket.setApiSymbol(SysConstants.CARDNAME);
		sysInTicket.setMid(mid);
		sysInTicket.setWxTicket(jsapi_ticket);
		sysInTicket.setExpiryTime(timestamp);
		sysInTicketService.saveSysInTicket(sysInTicket);

		return jsapi_ticket;
	}

}
